// ----------------------------------------
// Filename: Config_Handler.c
// Description: The code that handles the ADC's configuration set
// Author: Jon Maloney
// Date: 17-June-2012
//

//************************************************************
//                    Include .h files
//************************************************************

#include <stdio.h>
#include <stdlib.h>
#include "mqueue.h"
#include "thread.h"
#include "Linked_List.h"
#include "Timer.h"
#include "Config_Handler.h"
#include "ADC.h"
#include "serial.h"

//************************************************************
//                    Include external references
//************************************************************

//************************************************************
//                    Define Statements
//************************************************************

//************************************************************
//                    Struct, Union, enum Statements
//************************************************************

//************************************************************
//                    Global variables and constants
//************************************************************

//SampleConfigurationType mSampleConfiguation[];
linkedList_Handle ADC_Sample_List;

//SampleConfigurationType mSampleConfiguation;
UINT8 NbSamples = 0;

//The fastest sampling frequency in the current configuration
SampleConfigurationType fundamentalSamplingFrequency;

//************************************************************
//                    Prototype Private Functions
//************************************************************

PRIORITY 			configHandler_GetConfigurationPriority		(T_SAMPLE_HANDLE * ADC_Channel														);
BOOL 				configHandler_CreateConfiguration			(SampleConfigurationType * mSampleConfiguation										);
BOOL 				configHandler_UpdateConfiguration			(SampleConfigurationType * ConfigurationToBeUpdated, 	linkedList_Handle listNode	);
UINT16 				configHandler_FindNewFundamentalFrequency	(SampleConfigurationType * newConfiguration											);
UINT16 				configHandler_FindHarmonic					(SampleConfigurationType * Configuration											);
void 				configHandler_UpdateHarmonics				(UINT16 harmonicMultiplyer, 							linkedList_Handle listNode	);
BOOL 				configHandler_DeleteConfiguration			(UINT8 sampleID																		);
linkedList_Handle 	configHandler_FindItemInList				(UINT8 sampleID																		);
void 				configHandler_InsertionSort					(linkedList_Handle listNode															);
//************************************************************
//                    Implementation
//************************************************************


//A function that handles the ADC configuration
//	Configurations get sent to the ADC via a queue.
//	This function waits to receive new configurations via the queue.
//
//	When a configuration is received the function must determine if
//	1. A current configuration needs to be updated.
//	2. An existing configuration needs to be deleted
//	3. If a new configuration needs to be created.
//	
//	Once this has been decided the function calls the appropriate
//	method to peform the operation needed.
//	
//	The function will then allert the scheduler of the update.
void ConfigHandler_HandleConfiguration( void * pvParameters )
{
	SampleConfigurationType mSampleConfiguation;
	linkedList_Handle listNode;
	xComPortHandle pxPort = xGetSerialHandle(2);	//Get the handler for UART2
	ADC_Init();
	
	Thread_Create(ADCSampleHandler, (void *)ADC_Sample_List, 200, configKERNEL_INTERRUPT_PRIORITY);

	for(;;)
	{
		//Wait for a configuration to be sent
		if(xSerialGet( pxPort, &mSampleConfiguation, configMAX_DELAY ) == pdTRUE)
		{
			//A new configuration was received and is contained in variable mSampleConfiguration
			
			//Is this a delete configuration?
			if(mSampleConfiguation.s.SamplePeriodOrFrequency == 0)
			{
				configHandler_DeleteConfiguration(mSampleConfiguation.s.sampleID);
			}
			else
			{	//Do we already have a configuration for this channel?
				
				//Find the configuration that needs updating (if it exists)
		 		listNode = configHandler_FindItemInList( mSampleConfiguation.s.sampleID );
				
				if(listNode != NULL)
					configHandler_UpdateConfiguration(&mSampleConfiguation, listNode);	//Update the existing configuration
				else
					configHandler_CreateConfiguration(&mSampleConfiguation);//A new configuration needs to be created
			}
		}
		else
		{	
			#if _DEBUG == 1
				/*Nothing was received from the queue, blocking time has expired.*/
				ENTER_CRITICAL();
				{
					printf( "ADC Configuration Queue timeout occured \n" );
					fflush(stdout);
				}
				EXIT_CRITICAL();
			#endif
		}
	}
}

// ----------------------------------------
// configHandler_CreateConfiguration
// 
// Creates a new configuration based on the 
//	sampleConfiguration. The sampleConfiguration is a
//	structure describing the sampling requirements.
//	The configuraiton (T_SAMPLE_HANDLE) describes
//	how the sample will be configured
//
// Input:
//	mSampleConfiguation:	A structure containing a description of the
//							sampling requirements
// Output:
//  true:		If the configuration was created.
//	false: 		If the configuration was NOT created.
// Conditions:
// 	none
BOOL configHandler_CreateConfiguration(SampleConfigurationType * mSampleConfiguation)
{
	T_SAMPLE_HANDLE * ADC_Channel;
	linkedList_Handle currListNode;
	
	//Get memory from the heap for a new sample handle
	ADC_Channel = (T_SAMPLE_HANDLE *)malloc(sizeof(T_SAMPLE_HANDLE));

	//if there is no memory left in the heap
	if(ADC_Channel == NULL)
		return pdFALSE;
	
	/*The Queue length is 5* the number of samples taken per ADC interrupt.
	This ensures that the Queue can be flushed before it gets full.*/
	ADC_Channel->xADCQueue = mqueue_Create( 5*NbSamples, sizeof( SampleType ) );
	
	//Keep track of the sample ID
	ADC_Channel->_CHAN = mSampleConfiguation->s.sampleID;

	//Create a list item to put the sample configuration into
	currListNode = LinkedList_Add(ADC_Sample_List, (void *)ADC_Channel);

	//Insert the configuration into the existing 
	//	configuration set.
	configHandler_UpdateConfiguration(mSampleConfiguation, currListNode);

	//Create a thread to handle samples on this channel
	//	The priority of the thread is inversly proportional to 
	//	the sampling frequency.
	ADC_Channel->sampleThread =  Thread_Create(ADC_process_Sample, (void *)ADC_Channel->xADCQueue, 200, configHandler_GetConfigurationPriority(ADC_Channel));

	//Increment the count of number of signals being sampled
	NbSamples++;
	
	return pdTRUE;
}

// ----------------------------------------
// configHandler_GetConfigurationPriority
// 
// Obtains a unique priority for the configuration based
//	on its sampling frequency and the frequency of other
//	configurations in the configuration set.
//
//	A configuration sampling at the fastest frequency
//	will be given the highest priority.  A configuration
//	sampling a the lowest frequency will be given the 
//	lowest priority. 
//
//
// Input:
//	ADC_Channel:	A description of the configuration
// Output:
//  the configurations priority.
// Conditions:
// 	none
PRIORITY configHandler_GetConfigurationPriority(T_SAMPLE_HANDLE * ADC_Channel)
{
	//We assume that the ADC_Sample_List is ordered 
	// fastest frequency at the start of the list
	//	slowest frequency at the end of the list
	PRIORITY configurationPriority = configKERNEL_INTERRUPT_PRIORITY -2;
	linkedList_Handle tempConfig_list = ADC_Sample_List->PreviousNode;

	do
	{
		//If the item in the linked list matches 
		//	the ADC_Channel we have found Item
		if(tempConfig_list->Item == ADC_Channel)
			break;
		
		configurationPriority--;
	}while(ADC_Sample_List != tempConfig_list);	//Loop through the entire list of configurations

	if(configurationPriority == 0)
		configurationPriority = 1;

	return configurationPriority;
}

// ----------------------------------------
// configHandler_InsertionSort
// 
//	Inserts a configuration node into the 
//	list of configurations. The order of the
//	configuration list is determined by the
//	sampling frequency. The higher the 
//	sampling frequency the higher the priority
//	of the configuration.
//
// Input:
//	listNode:	The list node to insert into the 
//				configuration list. 
// Output:
//  none
// Conditions:
// 	none
void configHandler_InsertionSort(linkedList_Handle listNode)
{
	T_SAMPLE_HANDLE * ADC_Channel;
	UINT16 chanHarmonic;
	UINT16 currChanHarmonic;
	linkedList_Handle tempConfig_list = ADC_Sample_List;
	
	ADC_Channel = (T_SAMPLE_HANDLE *)listNode->Item;
	currChanHarmonic = ADC_Channel->_NB_HARMONICS;
	//Scan through ADC_Sample_List to find the point of insertion
	do
	{
		//look at the next configuration in the list
		//	Look from the back of the list to the front
		tempConfig_list = (linkedList_Handle)tempConfig_list->PreviousNode;
		ADC_Channel = (T_SAMPLE_HANDLE *)tempConfig_list->Item;
		chanHarmonic = ADC_Channel->_NB_HARMONICS;

		//If the harmonic of the item we are currently looking at
		//	has a smaller harmonic than the configuration we are
		//	adding to the list.
		if( chanHarmonic < currChanHarmonic)
			break;//We have found the position to put the configuration
		
	}while(tempConfig_list != ADC_Sample_List);	//Only loop through the list once.

	//Place the configuration into the list at the point found
	LinkedList_Insert((linkedList_Handle)tempConfig_list->NextNode, listNode);

}

// ----------------------------------------
// configHandler_UpdateConfiguration
// 
// Updates the configuration set.
//	-Determines if a new configuraiton found
//		requires the configuration set to be updated
//	-If a configuration set needs to be updated
//		the function initiates the update for each 
//		configuration.
//	-If the configuration set does not require updating
//		the function updates the configuration provided 
//		in the funciton argument. It does this by finding
//		a position for the configuration in the configuration
//		set.
//
// Input:
//	sampleID:	A reference to the linked list node that contains the 
//				configuration to be updated.
// Output:
//  true:		If the configuration was update.
//	false: 		If the configuration was NOT updated.
// Conditions:
// 	none
BOOL configHandler_UpdateConfiguration(SampleConfigurationType * ConfigurationToBeUpdated, linkedList_Handle listNode)
{
	UINT16 currHarmonicMultiple;
	T_TIMER_RATE t_Rate;
	BOOL newFundamentalFrequency = pdFALSE;
	T_SAMPLE_HANDLE * sampleHandle = (T_SAMPLE_HANDLE *)listNode->Item;

	//Test to see if the configuration have a faster sampling frequency
	//	than our previous configuration?
	if(fundamentalSamplingFrequency.s.SampleUnit <= ConfigurationToBeUpdated->s.SampleUnit)
	{
		if(fundamentalSamplingFrequency.s.SampleUnit == ConfigurationToBeUpdated->s.SampleUnit && fundamentalSamplingFrequency.s.SamplePeriodOrFrequency > ConfigurationToBeUpdated->s.SamplePeriodOrFrequency)
			newFundamentalFrequency = pdFALSE;
		else
			newFundamentalFrequency = pdTRUE;
	}
	else
		newFundamentalFrequency = pdTRUE;
	
	//If the new configuration has a faster sampling frequency
	if(newFundamentalFrequency == pdTRUE)
	{
		//The new configuraiton is to become the systems new 
		//	fundamental frequency. We need to update the timings 
		//	of all the configurations so they are sampled at correct
		//	frequencies.
		
		//We assume that the old fundamental frequency is a harmonic of the new fundamental frequency
		currHarmonicMultiple = configHandler_FindNewFundamentalFrequency(ConfigurationToBeUpdated);
		
		//Update the harmonics of each of the channels being sampled
		configHandler_UpdateHarmonics(currHarmonicMultiple, listNode);
	
		//Make this configuraiton the highest priority item by 
		//	making it the first item in the linked list.
		ADC_Sample_List = listNode;

		//Set our fundamental frequency to the configuration just updated
		//	This instruction copies all the data not just the pointer.
		fundamentalSamplingFrequency = *ConfigurationToBeUpdated;
		
		//Setup a structure of data to pass the timer function
		t_Rate._UNITS 		= fundamentalSamplingFrequency.s.SampleUnit;
		t_Rate._FREQUENCY 	= fundamentalSamplingFrequency.s.SamplePeriodOrFrequency;
		
		//Setup/modify the timer to call ADC_StartSampling at the fundamental frequency
		Timer_SetupTimer3(&t_Rate, ADC_StartSampling);
	}
	else
	{
		//Find out which harmonic is closest to the current fundamental frequency.
		currHarmonicMultiple = configHandler_FindHarmonic(ConfigurationToBeUpdated);
		
		//Update the sample handle with the new harmonic
		sampleHandle->_NB_HARMONICS = currHarmonicMultiple;

		//Insert the list node into its required position
		configHandler_InsertionSort(listNode);
	}

	//We need to look a the configurations deadline and see if the queue length is appropriate
		
	return pdTRUE;
}

UINT16 configHandler_FindNewFundamentalFrequency(SampleConfigurationType * newConfiguration)
{
	UINT8 sampConfigUnitDiff = fundamentalSamplingFrequency.s.SampleUnit - newConfiguration->s.SampleUnit;
	UINT32 returnVar;
	
	if(sampConfigUnitDiff == 0)
		returnVar = fundamentalSamplingFrequency.s.SamplePeriodOrFrequency / newConfiguration->s.SamplePeriodOrFrequency;
	else if(sampConfigUnitDiff == 1)
		returnVar = (UINT32)fundamentalSamplingFrequency.s.SamplePeriodOrFrequency * (UINT32)DT_C_Array[fundamentalSamplingFrequency.s.SampleUnit] / (UINT32)newConfiguration->s.SamplePeriodOrFrequency ;
	else
		returnVar = 16384;//Maximum harmonic


	if(returnVar >= 16384)
		return 16384;
	else
		return returnVar;
}

UINT16 configHandler_FindHarmonic(SampleConfigurationType * Configuration)
{
	UINT8 sampConfigUnitDiff = fundamentalSamplingFrequency.s.SampleUnit - Configuration->s.SampleUnit;
	UINT32 returnVar;
	
	if(sampConfigUnitDiff == 0)
		returnVar = Configuration->s.SamplePeriodOrFrequency / fundamentalSamplingFrequency.s.SamplePeriodOrFrequency;
	else if(sampConfigUnitDiff == 1)
		returnVar = (UINT32)Configuration->s.SamplePeriodOrFrequency * (UINT32)DT_C_Array[fundamentalSamplingFrequency.s.SampleUnit] / (UINT32)fundamentalSamplingFrequency.s.SamplePeriodOrFrequency;
	else
		returnVar = 16384;//Maximum harmonic

	if(returnVar >= 16384)
		return 16384;
	else
		return returnVar;
}

void configHandler_UpdateHarmonics(UINT16 harmonicMultiplyer, linkedList_Handle listNode)
{
	linkedList_Handle currNode = (linkedList_Handle)listNode->NextNode;
	T_SAMPLE_HANDLE * currSample;
	
	//Loop through the configuration set
	//	update all the configurations except the one 
	//	that is the new fundamental frequency (listNode)
	while(listNode != currNode)
	{
		currSample = (T_SAMPLE_HANDLE *)currNode->Item;
		currSample->_NB_HARMONICS = currSample->_NB_HARMONICS * harmonicMultiplyer;
	}
}

// ----------------------------------------
// configHandler_DeleteConfiguration
// 
// Deletes a configuration specified by the 
//	sampleID (ADC channelID)
//
// Input:
//	sampleID:	A reference to the configuration to be deleted.
// Output:
//  true:		If the configuration was deleted
//	false: 		If the configuration was not deleted (could not be found).
// Conditions:
// 	none
BOOL configHandler_DeleteConfiguration(UINT8 sampleID)
{
	//Find the handle to the link list that we want to delete
	linkedList_Handle list_Handle = configHandler_FindItemInList(sampleID);
	//Get the sample handle from the list.
	T_SAMPLE_HANDLE * sampleHandle = (T_SAMPLE_HANDLE *)list_Handle->Item;
	
	//If the handle is not null
	if(list_Handle != NULL)
	{
		//Delete the queue
		mqueue_Delete(sampleHandle->xADCQueue);
		
		//Delete the thread
		Thread_Delete(sampleHandle->sampleThread);

		//Delete the link list element.
		LinkedList_DeleteNode(list_Handle);
		
		//Decrement the number of channels being sampled
		ENTER_CRITICAL();
			NbSamples--;
		EXIT_CRITICAL();
		return pdTRUE;	//The configuration was successfully deleted
	}
	
	return pdFALSE;

}

// ----------------------------------------
// configHandler_FindItemInList
// 
// Finds the configuration in the list by referencing
//	the configurations sampleID (ADC channel being sampled).
//	Then returns a reference to the link list node that contains
//	the configuration.
//
// Input:
//	sampleID:	A reference to the configuration saught.
// Output:
//  			A reference to a linked list that contains
//				the configuration saught.
//	NULL:		If the item could not be found in the linked
//				list. 
//	NULL:		If there are no configurations in the configuration
//				set.
// Conditions:
// 	none
linkedList_Handle configHandler_FindItemInList(UINT8 sampleID)
{
	T_SAMPLE_HANDLE * sampleHandle;
	linkedList_Handle currListNode = ADC_Sample_List; 
	
	//If the configuration set is empty return null
	if(currListNode == NULL)
		return NULL;

	//Loop through the link list to find the configuration that we need
	do
	{
		sampleHandle = (T_SAMPLE_HANDLE *)currListNode->Item;
		if(sampleHandle->_CHAN == sampleID)
			return currListNode; //Return a handle to the link list containing the sample handle that was found
		
		currListNode = (linkedList_Handle)currListNode->NextNode;
	}while(currListNode != ADC_Sample_List);
	
	return NULL;	//Configuration could not be found
}
